//
// Created by baigui on 2021/10/23.
//

#include <cassert>
#include <common.h>
#include <log.h>
#include "include/BaseStream.h"

ssize_t BaseStream::sp_stream_ReadRaw(void *buf, size_t len) {

    ssize_t ret;

    assert(len <= SSIZE_MAX);

    /**
     * 这是支持块设备读取模式，这里只实现基本的read和seek
     */
    ret = read(buf, len);
    return ret;

}

ssize_t BaseStream::sp_stream_Read(void *buf, size_t len) {

    size_t copied = 0;

    while (len > 0)
    {
        ssize_t ret = sp_stream_ReadPartial(buf, len);
        if (ret < 0)
            continue;
        if (ret == 0)
            break;

        if (buf != nullptr)
            buf = (char *)buf + ret;
        assert(len >= (size_t)ret);
        len -= ret;
        copied += ret;
    }

    return copied;
}

ssize_t BaseStream::sp_stream_ReadPartial(void *buf, size_t len) {


    ssize_t ret = -1;

    if (peek != nullptr ) {
        int size = peek->getDataSize() - peek->getDataStart();
        if (size > 0) {
            if (len > size) {
                memcpy(buf, peek->getbuffer() + peek->getDataStart(), size);
                delete peek;
                peek = nullptr;
                offset += size;
                return size;
            } else {
                memcpy(buf, peek->getbuffer() + peek->getDataStart(), len);
                peek->setDataStart(peek->getDataStart() + len);
                offset += len;
                return len;
            }
        }
    }

    ret = sp_stream_ReadRaw(buf, len);
    if (ret > 0)
        offset += ret;
    if (ret == 0)
        eof = len != 0;
    assert(ret <= (ssize_t)len);
    return ret;
}

ssize_t BaseStream::sp_stream_Peek(uint8_t **bufp, size_t len) {

    block_t *peek1 = this->peek;

    if (peek1 == nullptr)
    {
        peek1 = new block_t(len);
    }
    else if (peek1->getTotalSize() < len)
    {
//        size_t avail = peek1->getDataSize();

        block_t *tmp = new block_t(peek1,len);
        delete peek1;
        peek1 = tmp;

    } else if (peek1->getDataStart() != 0){
        memcpy(peek1->getbuffer(),peek1->getbuffer()+peek1->getDataStart(),peek1->getDataSize() - peek1->getDataStart());
        peek1->setDataStart(0);
        peek1->setDataSize(peek1->getDataSize() - peek1->getDataStart());
    } else{
        //这是剩余空间够用。
    }

    *bufp = peek1->getbuffer() + peek1->getDataStart();

    while (peek1->getDataSize() - peek1->getDataStart() < len)
    {
        size_t avail = peek1->getDataSize() - peek1->getDataStart();
        ssize_t ret;

        ret = sp_stream_ReadRaw(peek1->getbuffer() + peek1->getDataSize(), len - avail);

        if (ret < 0)
            continue;

        peek1->setDataSize(peek1->getDataSize() + ret);

        if (ret == 0)
            break;
    }
    this->peek = peek1;
    return len;
}

block_t *BaseStream::sp_stream_ReadBlock() {
    
//    block_t *block;
//
//
//    //peak 是类似与缓存数据。
//    if (peek != nullptr)
//    {
//        block = peek;
//        peek = nullptr;
//    }
//    else if (this->block != nullptr)
//    {
//        block = this->block;
//        block = nullptr;
//    }
//    else
//    {
//        block = new block_t(4096);
//
//        ssize_t ret = read(block->getbuffer(), block->getTotalSize());
//        if (ret > 0)
//            block->setDataSize(ret);
//        else
//        {
//            delete(block);
//            block = nullptr;
//        }
//
//        eof = !ret;
//    }
//
//    if (block != nullptr)
//        offset += block->getDataSize();
//
//    return block;
}

uint64_t BaseStream::sp_stream_Tell() {

    return offset;
}

int BaseStream::sp_stream_Seek(uint64_t offset) {

    eof = false;

//    block_t *peek = peek;
//    if (peek != nullptr)
//    {
//        if (offset >= offset
//            && offset <= (offset + peek->i_buffer))
//        {   /* Seeking within the peek buffer */
//            size_t fwd = offset - offset;
//
//            peek->p_buffer += fwd;
//            peek->i_buffer -= fwd;
//            offset = offset;
//
//            if (peek->i_buffer == 0)
//            {
//                peek = nullptr;
//                delete(peek);
//            }
//
//            return sp_SUCCESS;
//        }
//    }
//    else
//    {
//        if (offset == offset)
//            return sp_SUCCESS; /* Nothing to do! */
//    }


    int ret = seek(offset);
    if (ret != SP_SUCCESS)
        return ret;

    offset = offset;

    if (peek != nullptr)
    {
        delete(peek);
        peek = nullptr;
    }


    return SP_SUCCESS;
}

int BaseStream::sp_stream_vaControl(int cmd, va_list args) {
    return 0;
}


block_t *BaseStream::sp_stream_Block(size_t size) {
    if( size > SSIZE_MAX )
        return nullptr;

    block_t *block = new block_t( size );

    ssize_t val = sp_stream_Read( block->getbuffer(), size );
    if( val <= 0 )
    {
        delete block ;
        return nullptr;
    }

    block->setDataSize(val);
    return block;
}

ssize_t BaseStream::sp_stream_CopyBlock(block_t *pp, void *buf, size_t len) {
    return 0;
}

